<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>Three.js 写实水面（Water2 + Sky）</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
  html,body{margin:0;height:100%;overflow:hidden;background:#000}
  #tips{position:absolute;left:12px;top:12px;color:#eaf6ff;
        font:13px/1.35 system-ui,Segoe UI,Roboto,Helvetica,Arial;
        background:rgba(0,0,0,.45);padding:8px 10px;border-radius:8px}
  #tips code{color:#9fe1ff}
</style>
<script type="importmap">
{
  "imports": {
    "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
    "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
  }
}
</script>
</head>
<body>
<div id="tips">
  <div>拖拽旋转，滚轮缩放</div>
  <div>方向(度)：<code id="deg">60</code>　速度：<code id="spd">0.35</code>　波尺度：<code id="scl">3.0</code></div>
  <div>想更“河流感”，把尺寸调小、方向与河道方向一致即可</div>
</div>

<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { Water } from 'three/addons/objects/Water2.js';
import { Sky } from 'three/addons/objects/Sky.js';

//// ===== 可调参数（一眼能看到真实水） =====
const FLOW_DIR_DEG = 60;     // 流动方向（0=+X, 90=+Z）
const FLOW_SPEED   = 0.35;   // 流速(0.0~1.0)
const WAVE_SCALE   = 3.0;    // 波尺度(越大波越密)
document.getElementById('deg').textContent = FLOW_DIR_DEG;
document.getElementById('spd').textContent = FLOW_SPEED.toFixed(2);
document.getElementById('scl').textContent = WAVE_SCALE.toFixed(1);

//// ===== 基础场景 =====
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(55, innerWidth/innerHeight, 0.1, 1e7);
camera.position.set(150, 90, 150);

const renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
renderer.outputColorSpace = THREE.SRGBColorSpace;
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 5, 0);

//// ===== 天空和物理日照（用于真实反射） =====
const sky = new Sky();
sky.scale.setScalar(100000);
scene.add(sky);

// 日照参数
const sun = new THREE.Vector3();
const pmrem = new THREE.PMREMGenerator(renderer);
const skyUniforms = sky.material.uniforms;
skyUniforms['turbidity'].value = 2.0;
skyUniforms['rayleigh'].value = 0.85;
skyUniforms['mieCoefficient'].value = 0.003;
skyUniforms['mieDirectionalG'].value = 0.85;

// 设置太阳高度/方位
function setSun(elevationDeg=30, azimuthDeg=120){
  const phi   = THREE.MathUtils.degToRad(90 - elevationDeg);
  const theta = THREE.MathUtils.degToRad(azimuthDeg);
  sun.setFromSphericalCoords(1, phi, theta);
  sky.material.uniforms['sunPosition'].value.copy(sun);
  scene.environment?.dispose?.();
  scene.environment = pmrem.fromScene(sky).texture;
}
setSun(35, 140);

//// ===== 写实水面（Water2） =====
// 需要水法线贴图；用官方例子贴图（CDN）
const waterNormals = new THREE.TextureLoader().load(
  'https://unpkg.com/three@0.160.0/examples/textures/waternormals.jpg',
  t => { t.wrapS = t.wrapT = THREE.RepeatWrapping; }
);

const waterGeom = new THREE.PlaneGeometry(8000, 8000);
const water = new Water(waterGeom, {
  color:        0x1a5fb4,      // 水体基色
  scale:        WAVE_SCALE,    // 波尺度
  flowDirection:new THREE.Vector2(
                   Math.cos(THREE.MathUtils.degToRad(FLOW_DIR_DEG)),
                   Math.sin(THREE.MathUtils.degToRad(FLOW_DIR_DEG))
                 ),
  textureWidth: 1024,
  textureHeight:1024,
  flowSpeed:    FLOW_SPEED,    // 流速（Water2 支持）
  normalMap0:   waterNormals,  // 双法线图可都用同一张
  normalMap1:   waterNormals
});
water.rotation.x = -Math.PI / 2;
water.position.y = 0;
scene.add(water);

//// ===== 放点参照物（岸石/桥） =====
const island = new THREE.Mesh(
  new THREE.CylinderGeometry(60, 180, 30, 48),
  new THREE.MeshStandardMaterial({ color:0x6d7d5c, roughness:0.95, metalness:0 })
);
island.position.set(-90, 15, -40);
scene.add(island);

const pier = new THREE.Mesh(
  new THREE.BoxGeometry(120, 10, 32),
  new THREE.MeshStandardMaterial({ color:0x8b7256, roughness:0.85 })
);
pier.position.set(80, 5, 40);
scene.add(pier);

// 一些远处山体剪影
const hill = new THREE.Mesh(
  new THREE.SphereGeometry(900, 32, 16),
  new THREE.MeshStandardMaterial({ color:0x1b2838, roughness:1 })
);
hill.scale.y = 0.25;
hill.position.set(0, -150, -1200);
scene.add(hill);

//// ===== 动画循环 =====
function animate(){
  controls.update();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
animate();

//// ===== 自适应窗口 =====
addEventListener('resize', () => {
  camera.aspect = innerWidth/innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
